/*
 * Copyright 2011-2012 Benoit Averty, Samuel Babin, Matthieu Bergere, Thomas Letan, Sacha Percot-Tétu, Florian Teyssier
 * 
 * This file is part of DETIQ-T.
 * 
 * DETIQ-T is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * DETIQ-T is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with DETIQ-T.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "GenericInterface.h"
#include "StandardImageWindow.h"
#include "UnknownFormatException.h"
#include "GraphicalHistogram.h"

#include "Algorithm/RgbToGrayscale.h"
#include "Algorithm/Otsu.h"
#include "GridView.h"

#include <QPushButton>
#include <QMessageBox>
#include <Converter.h>

using namespace genericinterface;
using namespace imagein;
using namespace std;
using namespace imagein::algorithm;

StandardImageWindow::StandardImageWindow(const QString path)
    : ImageWindow(path)
{
    bool error = false;
    QString msg = "";
    imagein::Image* image;
    try {
        image = new Image(path.toStdString());
    }
    catch(const imagein::UnknownFormatException& e) {
        error = true;
        msg = tr("Unknown file format !");
    }
    catch(const imagein::ImageFileException& e) {
        error = true;
        msg = QString::fromStdString(e.getMsg());
    }
    catch(const char* e) {
        error = true;
        msg = e;
    }
    catch(...) {
        error = true;
        msg = tr("Unknown exception");
    }
    
    if(error) {
        QMessageBox::critical(this, tr("Error while opening file"), msg);
        image = new Image();
    }

    _image = Converter<Image_t<int> >::convert(*image);
    QString title = ImageWindow::getTitleFromPath(path);
    QString ext;
    if(title.contains('.')){
        ext = title.right(title.size()-title.indexOf('.')) ;        
    }
    _extension=ext;
    _name = title.remove(_extension);

    this->setDisplayImage();
    
    this->setWindowTitle(_extension +tr(": ")+ _name);
    
    init();
}

StandardImageWindow::StandardImageWindow(Image_t<int> * image, const QString path)
    : ImageWindow(path, NULL)
{
    _image = image;

    this->setDisplayImage();
    this->setWindowTitle(ImageWindow::getTitleFromPath(path));

    init();
}

StandardImageWindow::StandardImageWindow(const StandardImageWindow& siw, Image_t<int> * image)
    : ImageWindow(siw.getPath())
{
    /*replace by makeDisplayable
    if(image == NULL) {
        image = new Image(*siw._image);
    }
    */

    _image = image;
    this->setDisplayImage(this->CONS);

    this->setWindowTitle(siw.windowTitle());

    init();
}

StandardImageWindow::~StandardImageWindow()
{
    delete _image;
}


void StandardImageWindow::init()
{
    QObject::connect(this->view(), SIGNAL(updateSrc(GenericHistogramView*,imagein::Rectangle)), this, SLOT(updateSrc(GenericHistogramView*,imagein::Rectangle)));

    menu()->addAction(tr("Histogram"), this, SLOT(showHistogram()));
    menu()->addAction(tr("Cumulated histogram"), this, SLOT(showCumulatedHistogram()));
    menu()->addAction(tr("Horizontal Projection Histogram"), this, SLOT(showHProjectionHistogram()));
    menu()->addAction(tr("Vertical Projection Histogram"), this, SLOT(showVProjectionHistogram()));
    menu()->addAction(tr("Pixels Grid"), this, SLOT(showPixelsGrid()));
    menu()->addAction(tr("Column Profile"), this, SLOT(showColumnProfile()));
    menu()->addAction(tr("Line Profile"), this, SLOT(showLineProfile()));
    menu()->addSeparator();
    menu()->addAction(tr("Crop"), this, SLOT(crop()));
    menu()->addAction(tr("Copy & crop"), this, SLOT(copycrop()));
    menu()->addSeparator();
    menu()->addAction(tr("Convert to grayscale"), this, SLOT(convertToGrayscale()));
    menu()->addAction(tr("Convert to binary"), this, SLOT(convertToBinary()));

    updateStatusBar();


}


void StandardImageWindow::updateSpecStatusBar(){
    typeoftheimage->setText(QString(tr("Integer")));

    QString stats("min : %1\t max : %2\t mean : %3\t standard deviation : %4");
    QString min="", max="", mean="", dev="";
    for(unsigned int c = 0; c < _image->getNbChannels(); ++c) {
        min += QString("%1").arg(_image->min(c));
        max += QString("%1").arg(_image->max(c));
        mean += QString("%1").arg(_image->mean(c), 0, 'f', 1);
        dev += QString("%1").arg(_image->deviation(c), 0, 'f', 1);
        if(c < _image->getNbChannels()-1)  {
            min+=" "; max+=" "; mean+=" "; dev+=" ";
        }
    }
    stats = stats.arg(min).arg(max).arg(mean).arg(dev);
    _lStats->setText(stats);
}


void StandardImageWindow::showGenericHistogram(GenericHistogramWindow* histogramWnd){

    _imageView->setSelectSrc(histogramWnd->getView());
    QObject::connect(histogramWnd, SIGNAL(selectRectChange(imagein::Rectangle, GenericHistogramView*)), _imageView, SLOT(showSelectRect(imagein::Rectangle, GenericHistogramView*)));
   
//    WindowService* ws = dynamic_cast<WindowService*>(_gi->getService(GenericInterface::WINDOW_SERVICE));
//    ws->addWidget(ws->getNodeId(this), histogramWnd);

    emit addWidget(this, histogramWnd);
}

void StandardImageWindow::showHistogram()
{
    HistogramWindow* histogramWnd = new HistogramWindow( _displayImg, selection(), this->windowTitle());
    showGenericHistogram(histogramWnd);
}

void StandardImageWindow::showCumulatedHistogram(){
    HistogramWindow* histogramWnd = new HistogramWindow( _displayImg, selection(), this->windowTitle(), true);
    showGenericHistogram(histogramWnd);
}

void StandardImageWindow::showHProjectionHistogram()
{
    bool ok;
    int value = QInputDialog::getInt(this, tr("Select value"), tr("Which value (0..255) ?"), 0, 0, 255, 1, &ok);

    if (ok)
    {
        ProjectionHistogramWindow* histogramWnd = new ProjectionHistogramWindow(_displayImg, selection(), value, true, this->windowTitle());
        showGenericHistogram(histogramWnd);
    }
}

void StandardImageWindow::showVProjectionHistogram()
{
	bool ok;
	int value = QInputDialog::getInt(this, tr("Select value"), tr("Which value (0..255) ?"), 0, 0, 255, 1, &ok);
	
	if(ok)
	{
        ProjectionHistogramWindow* histogramWnd = new ProjectionHistogramWindow(_displayImg, selection(), value, false,  this->windowTitle());
        showGenericHistogram(histogramWnd);
	} 
}

void StandardImageWindow::showLineProfile()
{
    imagein::Rectangle rect(0, _selectedPixel.y(), (_displayImg)->getWidth(), 1);
    RowWindow* histogramWnd = new RowWindow(_displayImg, rect, false, this->windowTitle() + QString(" - ") + tr("Line Profile"));
    showGenericHistogram(histogramWnd);
}

void StandardImageWindow::showColumnProfile()
{
    imagein::Rectangle rect(_selectedPixel.x(), 0, 1, _displayImg->getHeight());
    RowWindow* histogramWnd = new RowWindow(_displayImg, rect, true, this->windowTitle() + QString(" - ")  + tr("Line Profile"));
    showGenericHistogram(histogramWnd);
}

void StandardImageWindow::showPixelsGrid()
{
    GridView* grid = new GridView(_image,_displayImg);
    grid->setWindowTitle(this->windowTitle() + QString(" - ")  + tr("Pixels Grid"));
    emit addWidget(this, grid);
}

void StandardImageWindow::crop() {
    const Image_t<int>* oldImg = _image;
    Image_t<int>* newImg = oldImg->crop((_imageView)->getRectangle());
    _image = newImg;
    this->setDisplayImage(this->CONS);

    delete oldImg;
    view()->update();
    this->adjustSize();
    this->updateGeometry();
}

void StandardImageWindow::copycrop() {
    const Image_t<int>* oldImg = _image;
    Image_t<int>* newImg = oldImg->crop((_imageView)->getRectangle());
    StandardImageWindow* newImgWnd = new StandardImageWindow(*this, newImg);
    emit addImage(this, newImgWnd);
}

void StandardImageWindow::convertToGrayscale() {
    GrayscaleImage* newImg = RgbToGrayscale()(Converter<RgbImage>::convert(*_displayImg));
    Image_t<int>* newImg2 = Converter<Image_t<unsigned char> >::convertToInt(*newImg);
    StandardImageWindow* newImgWnd = new StandardImageWindow(*this, newImg2);

    emit addImage(this, newImgWnd);
}


void StandardImageWindow::convertToBinary() {
    GrayscaleImage* newImg = Otsu()(Converter<GrayscaleImage>::convert(*_displayImg));
    Image_t<int>* newImg2 = Converter<Image_t<unsigned char> >::convertToInt(*newImg);
    StandardImageWindow* newImgWnd = new StandardImageWindow(*this, newImg2);

    emit addImage(this, newImgWnd);
}

void StandardImageWindow::showSelectedPixelInformations(int x, int y) const
{
    showPixelInformations(x, y, _lSelectedPixelPosition,_lSelectedPixelRGBColor,_lSelectedPixelHSVColor,_lSelectedPixelHSLColor);
}

void StandardImageWindow::showHoveredPixelInformations(int x, int y) const
{
    showPixelInformations(x, y, _lHoveredPixelPosition,_lHoveredPixelRGBColor,_lHoveredPixelHSVColor,_lHoveredPixelHSLColor);
}

void StandardImageWindow::showPixelInformations(int x, int y, QLabel *labelpos,QLabel *labelrgb,QLabel *labelhsl,QLabel *labelhsv) const{
    labelpos->setText(QString("%1x%2").arg(x).arg(y));
    unsigned char red=0, green=0,blue=0;

    for(unsigned int i = 0; i < _image->getNbChannels(); i++){
        try {
            unsigned char temp = _displayImg->getPixel(x, y, i);
            switch(i){
            case 0 : red   = temp;break;
            case 1 : green = temp;break;
            case 2 : blue  = temp;break;
            }
             }
             catch(std::out_of_range&) {
        }
    }

    //The value in RGB
    QColor RGB = QColor::fromRgb(red,green,blue);
    labelrgb->setText(QString(" (RGB) %1 %2 %3").arg(RGB.red(),3,'f',0,' ').arg(RGB.green(),3,'f',0,' ').arg(RGB.blue(),3,'f',0,' ') );

    //the HSL color depend on RGB
    QColor HSV = RGB.toHsv();
    labelhsv->setText(QString(" (HSV) %1 %2 %3").arg(HSV.hue(),3,'f',0,' ').arg(HSV.saturation(),3,'f',0,' ').arg(HSV.value(),3,'f',0,' ') );

    //the HSL color depend on RGB
    QColor HSL = RGB.toHsl();
    labelhsl->setText(QString(" (HSL) %1 %2 %3").arg(HSL.hue(),3,'f',0,' ').arg(HSL.saturation(),3,'f',0,' ').arg(HSL.lightness(),3,'f',0,' ') );

}

void StandardImageWindow::updateSrc(GenericHistogramView* histo, imagein::Rectangle rect) {
    histo->update(_displayImg, rect);
}

Image* StandardImageWindow::makeDispImage(Mode mode){
    Image * image = new Image();
    if(mode == this->CONS){
        image = Converter<Image_t<int> >::makeDisplayable(_image,NULL);
    }else if(mode == this->USER){
        image =  Converter<Image_t<int> >::makeDisplayable(_image,_param);
    }
    return image;
}

void StandardImageWindow::setMemoryImage(Image_t<int> * image){
    _image = image;
    this->setDisplayImage(this->USER);
}


